/**
 * 
 */
package com.wgo.precise.server.service;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.util.List;

import org.apache.log4j.Logger;
import org.apache.xmlbeans.XmlException;
import org.apache.xmlbeans.XmlOptions;

import remato.domain.common.RequirementSystem;
import remato.domain.common.impl.RequirementSystemImpl;

import com.lowagie.text.Document;
import com.lowagie.text.DocumentException;
import com.lowagie.text.HeaderFooter;
import com.lowagie.text.PageSize;
import com.lowagie.text.Phrase;
import com.lowagie.text.pdf.PdfWriter;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.xml.DomDriver;
import com.wgo.bpot.common.removeme.Persistent;
import com.wgo.bpot.common.transport.exception.RematoException;
import com.wgo.bpot.common.transport.util.DomainModelTypeNamingConvention;
import com.wgo.bpot.server.persist.PersistService;
import com.wgo.precise.server.export.ExportPrecise;
import com.wgo.precise.server.export.ImportPrecise;
import com.wgo.precise.server.pdf.EndOfPage;
import com.wgo.precise.server.pdf.PdfFactory;
import com.wgo.precise.server.pdf.PdfFactoryRegistry;
import com.wgo.precise.server.preciseData.RequirementSystemDocument;
import com.wgo.precise.server.web.servlet.PdfServlet;
import com.wgo.precise.service.PreciseService;

/**
 * @author peide3
 *
 */
public class PreciseServiceServerImpl implements PreciseService {

	private static Logger logger = Logger.getLogger(PreciseServiceServerImpl.class);
	
	private static EndOfPage endOfPage = new EndOfPage();
	
	private final static Class<? extends RequirementSystem> rootConceptClass = RequirementSystem.class;
	private final static Long rootConceptIdentifier = new Long(-1); // TODO: reciece from somewhere else...
	
	private PdfFactoryRegistry pdfFactoryRegistry;

	private PersistService persistService;
	
	
	static {
		endOfPage.setEndOfPageText("Precise Requirement System");
	}
	
	public PreciseServiceServerImpl() {
		pdfFactoryRegistry = new PdfFactoryRegistry();
		XStream xs = new XStream(new DomDriver());
		InputStream is = PdfServlet.class.getResourceAsStream("/pdf-factories.xml");
		if (null == is) {
			logger.info("No PDF factories defined.  /pdf-factories.xml file does not exist in classpath.");
		}
		List<String> pdfFactoryClassNames = null;
		try {
			pdfFactoryClassNames = (List<String>)xs.fromXML(is);
			for (String factoryClassName : pdfFactoryClassNames) {
				pdfFactoryRegistry.addFactory(DomainModelTypeNamingConvention.<PdfFactory>loadClass(factoryClassName).newInstance());
			}
			pdfFactoryRegistry.initFactories();
		} catch (Exception e) {
			logger.warn("Found /pdf-factories.xml file, but it has invalid format! I will consider this as if no triggers are defined.",e);
			return;
		}
	}

	public boolean importFromXml(byte[] preciseXmlFile) {
		logger.info("PreciseServiceServerImpl.importFromXml(File preciseXmlFile) called");
		ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(preciseXmlFile);
		RequirementSystemDocument requirementSystemDocument = null;
		try {
			requirementSystemDocument = RequirementSystemDocument.Factory.parse(byteArrayInputStream);
		} catch (XmlException e) {
			throw new RematoException("Unable to import XML at server, invalid XML", e);
		} catch (IOException e) {
			throw new RematoException("Unable to import XML at server, error transfering", e);
		}
		persistService.createSchema();
		ImportPrecise importDocument = new ImportPrecise();
		RequirementSystem requirementSystem = importDocument.readDocument(requirementSystemDocument.getRequirementSystem());
		RequirementSystem persistedRequirementSystem = persistService.getInstance(requirementSystem.getClass(), rootConceptIdentifier);
		((RequirementSystemImpl)persistedRequirementSystem).setProjectChildren(requirementSystem.getProjectChildren()); // TODO: remove this and convert by reflection... (then it will be possible to use other backend than ejb..(typically modify alogorith in server model converter))
		((RequirementSystemImpl)persistedRequirementSystem).setStakeholders(requirementSystem.getStakeholders());
		((RequirementSystemImpl)persistedRequirementSystem).setPropertySets(requirementSystem.getPropertySets());
		persistService.saveOrUpdate(persistedRequirementSystem);
		return true;
	}

	public byte[] exportToXml() {
		logger.info("PreciseServiceServerImpl: public File exportToXml() called");
		ExportPrecise exportPrecise = new ExportPrecise();
		RequirementSystemDocument requirementSystemDocument = exportPrecise.createDocument(persistService.getInstance(DomainModelTypeNamingConvention.loadServerClass(rootConceptClass), rootConceptIdentifier));
		ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
		XmlOptions xmlOption = new XmlOptions();
		xmlOption.setSavePrettyPrint();
		try {
			requirementSystemDocument.save(byteArrayOutputStream,xmlOption);
		} catch (IOException e) {
			throw new RematoException("Unable to create XML-document at server.", e);
		}
		return byteArrayOutputStream.toByteArray();
	}

	public byte[] getPdf(Class<? extends Persistent> conceptType, Serializable identifier) {
		logger.info("PreciseServiceServerImpl: public File exportToXml() called");

		Document doc = new Document();
		ByteArrayOutputStream baosPDF = new ByteArrayOutputStream();
		PdfWriter docWriter = null;
		Throwable throwable = null;
		try {
			docWriter = PdfWriter.getInstance(doc, baosPDF);
			docWriter.setPageEvent(endOfPage);
			doc.addAuthor(this.getClass().getName());
			doc.addCreationDate();
			doc.addProducer();
			doc.addCreator(this.getClass().getName());
			doc.addTitle("Precise Requirement");
			doc.setPageSize(PageSize.LETTER);
			HeaderFooter header = new HeaderFooter(new Phrase(
					"Precise Requirements"), false);
			doc.setHeader(header);
			doc.open();
			pdfFactoryRegistry.writeDocument(doc, conceptType.getSimpleName(), (Long)identifier);
		} catch (DocumentException dex) {
			baosPDF.reset();
			throwable = dex;
			throw new RematoException("Error exporting to PDF.",dex) ;
		} catch (Throwable e){
			throwable = e;
		} finally {
			try {
				if (doc != null) {
					doc.close();
				}
				if (docWriter != null) {
					docWriter.close();
				}
			}catch (Throwable e) {
				if (null != throwable) {
					e = throwable;
				}
				throw new RematoException("Could not create PDF at server!", e);
			}
		}
		if (baosPDF.size() < 1) {
			throw new RematoException ("Error exporting PDF. ", new DocumentException("document has " + baosPDF.size() + " bytes"));
		}
		return baosPDF.toByteArray();
	}

	public PersistService getPersistService() {
		return persistService;
	}

	public void setPersistService(PersistService persistService) {
		persistService.setRootConceptClass(rootConceptClass);
		this.persistService = persistService;
		this.pdfFactoryRegistry.setPersistService(persistService);
	}

}
